﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ExpressionMapper.Tests
{
    [TestClass]
    public class DowncastsTest
    {
        public abstract class Item
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }

        public sealed class MarketItem : Item
        {
            public string BarCode { get; set; }
        }

        public interface IContainer
        {
            int ItemId { get; set; }
            object Item { get; set; }
            string ItemName { get; }
        }

        public class SimpleContainer : IContainer
        {
            public readonly Guid UniqueGuid;

            public SimpleContainer()
            {
                UniqueGuid = new Guid();
            }

            public virtual int ItemId { get; set; }
            public object Item { get; set; }
            public virtual string ItemName
            {
                get { return Item.ToString(); }
            }
        }

        public sealed class ItemContainer : SimpleContainer
        {
            public new Item Item
            {
                get { return (Item)base.Item; }
                set { base.Item = value; }
            }
            public override int ItemId { get { return Item.Id; } }
            public override string ItemName { get { return Item.Name; } }
        }

        public sealed class MarketItemContainer : SimpleContainer
        {
            public new MarketItem Item
            {
                get { return (MarketItem)base.Item; }
                set { base.Item = value; }
            }
            public override int ItemId { get { return Item.Id; } }
            public override string ItemName { get { return Item.Name; } }
        }


        [TestMethod]
        public void ObjectTest()
        {
            SimpleContainer container = new SimpleContainer { Item = new object(), ItemId = 1 };
            var containerMapper = Mapper.Create<SimpleContainer>();
            var clone = containerMapper(container);
            validateClone(container, clone);
        }

        [TestMethod]
        public void ObjectDowncastTest()
        {
            SimpleContainer container = new SimpleContainer { Item = new MarketItem { BarCode = "1234", Id = 1, Name = "Apple" }, ItemId = 1 };
            var containerMapper = Mapper.Create<SimpleContainer>();
            var clone = containerMapper(container);
            validateClone(container, clone);
        }

        [TestMethod]
        public void ObjectDowncastTest2()
        {
            SimpleContainer container = new SimpleContainer { Item = new MarketItem { BarCode = "1234", Id = 1, Name = "Apple" }, ItemId = 1 };
            var containerMapper = Mapper.Create(typeof(object), typeof(SimpleContainer));
            object clone = containerMapper(container);
            validateClone(container, clone as SimpleContainer);
        }

        [TestMethod]
        public void AbstractClassDowncastTest()
        {
            SimpleContainer container = new ItemContainer { Item = new MarketItem { BarCode = "1234", Id = 1, Name = "Apple" } };
            var containerMapper = Mapper.Create<SimpleContainer>();
            var clone = containerMapper(container);
            validateClone(container, clone);
        }

        [TestMethod, ExpectedException(typeof(InvalidOperationException))]
        public void InterfaceDowncastTest()
        {
            IContainer container = new MarketItemContainer { Item = new MarketItem { BarCode = "1234", Id = 1, Name = "Apple" } };
            var containerMapper = Mapper.Create<IContainer>();
            var clone = containerMapper(container);
            validateClone(container, clone);
        }

        [TestMethod]
        public void ClassDowncastTest()
        {
            MarketItemContainer container = new MarketItemContainer { Item = new MarketItem { BarCode = "1234", Id = 1, Name = "Apple" } };
            var containerMapper = Mapper.Create<SimpleContainer>();
            var clone = containerMapper(container);

            validateClone(container, clone);
        }

        [TestMethod]
        public void DowncastDictionaryTest()
        {
            Dictionary<long, MarketItem> dictionary = new Dictionary<long, MarketItem>();
            dictionary.Add(1, new MarketItem { BarCode = "1", Id = 1, Name = "1" });
            dictionary.Add(2, new MarketItem { BarCode = "2", Id = 2, Name = "2" });
            dictionary.Add(3, new MarketItem { BarCode = "3", Id = 3, Name = "3" });
            dictionary.Add(4, new MarketItem { BarCode = "4", Id = 4, Name = "4" });
            dictionary.Add(5, new MarketItem { BarCode = "5", Id = 5, Name = "5" });

            var containerMapper = Mapper.Create(dictionary.GetType());
            var clone = containerMapper(dictionary);
            Assert.IsNotNull(clone);
            var clonedDictionary = clone as Dictionary<long, MarketItem>;
            Assert.IsNotNull(clonedDictionary);
            Assert.AreEqual(dictionary.Count, clonedDictionary.Count);
        }


        private static void validateClone(IContainer container, IContainer clone)
        {
            Assert.IsNotNull(clone);
            Assert.AreEqual(container.ItemId, clone.ItemId);
            Assert.IsNotNull(clone.Item);

            if (container.ItemName != clone.ItemName)
            {
                
                Assert.AreEqual(((Item)container.Item).Id, ((Item)clone.Item).Id);
                Assert.AreEqual(((Item)container.Item).Name, ((Item)clone.Item).Name);
                if(container.Item.GetType()==typeof(MarketItem))
                {
                    Assert.AreEqual(((MarketItem)container.Item).BarCode, ((MarketItem)clone.Item).BarCode);
                }
            }
            Assert.IsFalse(ReferenceEquals(container.Item, clone.Item));

        }
    }
}
